﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Input;
using Faz.SideWinderSC.Logic;

namespace Faz.SideWinderSC.Client.ViewModel
{
    /// <summary>
    /// Defines the view-model that provides the base information about a profile.
    /// </summary>
    public class ProfileViewModel : AbstractViewModel
    {
        /// <summary>
        /// The associated profile.
        /// </summary>
        private readonly Profile profile;

        /// <summary>
        /// The associated mapping.
        /// </summary>
        private readonly ICollection<MappingViewModel> mappings;

        private readonly ICommand save;

        private readonly ICommand restore;

        /// <summary>
        /// Initializes a new instance of the <see cref="ProfileViewModel"/> class.
        /// </summary>
        public ProfileViewModel(Profile profile)
        {
            if (profile == null)
            {
                throw new ArgumentNullException("profile");
            }

            this.profile = profile;
            this.mappings = new ViewModelCollection<MappingViewModel>(this.LoadMappings);
            ((INotifyCollectionChanged)this.mappings).CollectionChanged
                += this.OnMappingsCollectionChanged;

            this.Name = this.profile.Name;

            this.save = new Command(this.SaveExecute, this.CanSave);
            this.restore = new Command(this.RestoreExecute, this.CanRestore);
        }

        /// <summary>
        /// Gets or sets the name of the profile.
        /// </summary>
        public string Name
        {
            get { return this.GetValue<string>("Name"); }
            set { this.SetValue("Name", value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the profile is modified
        /// and requires a save.
        /// </summary>
        public bool IsModified
        {
            get { return this.GetValue<bool>("IsModified"); }
            set { this.SetValue("IsModified", value); }
        }

        /// <summary>
        /// Gets the associated mappings.
        /// </summary>
        public ICollection<MappingViewModel> Mappings
        {
            get { return this.mappings; }
        }

        public ICommand Save
        {
            get { return this.save; }
        }

        public ICommand Restore
        {
            get { return this.restore; }
        }

        private bool CanSave(object parameter)
        {
            return this.IsModified;
        }

        private bool CanRestore(object parameter)
        {
            return this.IsModified;
        }

        private void SaveExecute(object parameter)
        {
            this.profile.Name = this.Name;
            this.profile.Mappings.Clear();
            foreach (var mappingVM in this.mappings)
            {
                Mapping mapping = new Mapping()
                {
                    Button = mappingVM.Button,
                    Name = mappingVM.Name,
                };

                mapping.Keys.Copy(mappingVM.Keys);
                this.profile.Mappings.Add(mapping);
            }

            ServiceFactory.Instance.ModelService.SaveProfile(this.profile);
            this.IsModified = false;
        }

        private void RestoreExecute(object parameter)
        {
            this.Name = this.profile.Name;
            this.Mappings.Clear();

            foreach (var mapping in this.LoadMappings())
            {
                this.Mappings.Add(mapping);
            }

            this.IsModified = false;
        }

        /// <summary>
        /// Loads the mapping of the current profile.
        /// </summary>
        /// <returns>
        /// The view-model instances that encapsulate the associated mappings.
        /// </returns>
        private IEnumerable<MappingViewModel> LoadMappings()
        {
            return this.profile.Mappings.Select(m => new MappingViewModel()
            {
                Button = m.Button,
                Name = m.Name,
                Keys = m.Keys,
            });
        }

        private void OnMappingsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (MappingViewModel item in e.OldItems)
                {
                    item.PropertyChanged -= this.OnMappingPropertyChanged;
                }
            }

            if (e.NewItems != null)
            {
                foreach (MappingViewModel item in e.NewItems)
                {
                    item.PropertyChanged += this.OnMappingPropertyChanged;
                }
            }
        }

        private void OnMappingPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.IsModified = true;
        }
    }
}
